﻿#ifndef _C_SCRIPTMANAGER_H_INCLUDE_
#define _C_SCRIPTMANAGER_H_INCLUDE_

#include <QObject>
#include <QAction>
#include <QJSEngine>
#include <QFileSystemWatcher>
#include <QTimer>
#include <QUdpSocket>
#include <QMap>
#include <QWebSocket>
#include <QLibrary>

#include "../network/cwebsocketserver.h"
#include "../network/networkframemanager.h"
#include "../common/singleton.h"

/**
 * @brief The CJsManager class JS脚本管理类
 *
 * 注意：脚本文件一定要采用GBK编码，否则读取的将是乱码
 */
class CJsManager : public QObject ,
                   public NetworkFrameManager ,
                   public Singleton<CJsManager>
{
    Q_OBJECT

public:
    explicit CJsManager(QWidget *parent = nullptr);
    ~CJsManager();

    /// 初始化系统
    bool init_system(QString workingpath,
                     QString regdllpath="",
                     int debugserverport=-1,
                     bool isauotoloadscript=true,
                     bool isConnectServer=true,
                     bool isDebugMode=false);
    /// 卸载系统
    void clean_system(void);
    /// 清除所有的插件
    void cleanallplugin(void);

    /// 启动调试服务器
    Q_INVOKABLE bool startDebugServer(int serverport);
    /// 关闭调试服务器
    Q_INVOKABLE void closeDebugServer(void);
    /// 检测是否启动调试服务器
    Q_INVOKABLE bool isstardedDebugServer(void);

    /// 设置当前系统工作目录
    void setCurrentWorkingPath(QString ppath);
    /// 添加其它要监控的目录
    void addOtherWorkingPath(QString ppath);
    /// 得到当前系统工作目录
    inline QString getCurrentWorkingPath(void) { return m_currentWorkingPath; }
    /// 设置当前脚本注册的dll所在目录
    inline void setRegisterDllPath(QString ppath) { m_currentRegisterDllPath = ppath; }
    /// 得到当前脚本注册的dll所在目录
    inline QString getRegisterDllPath(void) { return m_currentRegisterDllPath; }
    /// 设置是否资源改变自动加载脚本
    inline void setAutoLoadScript(bool isautoload) { m_isAutoLoadScript = isautoload; }
    ///  得到是否自动加载脚本
    inline bool isAutoLoadScript(void) { return m_isAutoLoadScript; }
    /// 设置关联设备ID
    inline void setDeviceID(int pid) { m_DeviceID = pid; }
    /// 得到关联设备ID
    inline int getDeviceID(void) { return m_DeviceID; }
    /// 设置是否处于调试模式下
    inline void setEnableDebug(bool isenable) { m_isDebugMode = isenable; }
    /// 得到是否处于调试模式下
    inline bool isEnableDebug(void) { return m_isDebugMode; }
    /// 设置关联设备名称
    inline void setDeviceName(QString name) { m_DeviceName = name; }
    /// 得到关联设备名称
    inline QString getDeviceName(void) { return m_DeviceName; }
    /// 得到当前数据版本
    inline int getDataVerison(void) { return m_DataVersion; }
    /// 设置用户信息
    inline void setUserInfo(int uid,QString pwd) { m_UserID = uid; m_UserPassword = pwd; }
    /// 得到指定名称的插件
    tagpluginItem* getPluginByName(QString name);
    /// 设置是否支持加密读取
    inline void setEncryptedread(bool isEncrypted) { m_Encryptedread = isEncrypted; }
    /// 是否支持加密读取
    inline bool isEncryptedread(void) { return m_Encryptedread; }
    /// 添加一个模块到系统中
    Q_INVOKABLE void addModule(QString name,QString modulepath);
    /// 调用指定的模块
    Q_INVOKABLE QJSValue getModule(QString nudName);

    /// 启动远程桌面
    Q_INVOKABLE inline void setEnableRemoteDesktop(bool isEnable) { m_isEnableScreen = isEnable; }
    /// 是否启动远程桌面
    Q_INVOKABLE inline bool isEnableRemoteDesktop(void) { return m_isEnableScreen; }
    /// 设置远程桌面屏幕高宽
    Q_INVOKABLE inline void setDesktopSize(int width,int height) { m_ScreenWidth=width; m_ScreenHeight=height; }
    /// 设置屏幕录制频率
    Q_INVOKABLE inline void setRefreshRate(int rate) { m_refreshfrequency = rate; }
    /// 得到屏幕录制频率
    Q_INVOKABLE inline int getRefreshRate(void) { return m_refreshfrequency; }

    /// 设置反向代理服务器参数
    //Q_INVOKABLE void setReverseProxyServerParams(QString ipaddress,int port);
    /// 得到反向代理服务器IP
    Q_INVOKABLE inline QString getReverseProxyServerIP(void) { return m_ReverseProxyServerIP.toString(); }
    /// 得到反向代理服务器端口
    Q_INVOKABLE inline int getReverseProxyServerPort(void) { return m_ReverseProxyServerPort; }
    /// 得到控制设备的IP
    Q_INVOKABLE inline QString getControlDeviceIP(void) { return m_ControlDeviceIP.toString(); }
    /// 得到控制设备的端口
    Q_INVOKABLE inline quint16 getControlDevicePort(void) { return m_ControlDevicePort; }
    /// 向控制设备发送数据
    Q_INVOKABLE bool sendDataToControlDevice(QByteArray data,bool isprocessEvents=false);

    /// 发送日志信息
    Q_INVOKABLE void Log(QString msg, QString type="info",
                         bool isuseGBK=true,
                         bool isPrint=true,bool isSend=true,
                         bool isLog=true,bool isProg=false);

    /// 开始执行脚本
    void runScript(void);
    /// 从指定文件中导入脚本并执行
    bool js_loadscriptstring_callback(QString scriptfilepath,bool isCheckMd5=true);
    /// 初始脚本系统
    void js_setup_callback(void);
    /// 重置脚本系统
    void js_reset_callback(void);
    /// 执行指定名称的函数
    bool execute_function_callback(QString functionName,QJSValueList params=QJSValueList());
    ///注册一个新的对象
    QJSValue RegisterNewObject(QString objName,QObject *newobject,bool isCppOwnership=false);
    ///注册一个新的类
    QJSValue RegisterNewClass(QString className,const QMetaObject *newmetaObject);
    ///注册一个新的数组
    QJSValue RegisterNewArray(int arraySize);
    ///建立一个新的对象
    inline QJSValue CreateNewObject(QObject *object=NULL)
    {
        if(object)
            return m_JSEngine.newQObject(object);

        return m_JSEngine.newObject();
    }

    /// 处理网络字符串消息
    virtual void OnProcessNetText(QWebSocket *conn,QString mes);
    /// 处理网络二进制消息
    virtual void OnProcessNetBinary(QWebSocket *conn,QByteArray &data);
    /// 处理一个新的连接到达
    virtual void OnProcessConnectedNetMes(QWebSocket *conn);
    /// 处理一个连接关闭
    virtual void OnProcessDisconnectedNetMes(QWebSocket *conn);
    /// 处理二进制文件发送，接收进度，type:0发送，1接收
    virtual void OnProcessBinaryOperProcess(QWebSocket *conn,int type,qint64 sendsize,qint64 totalsize);

    /// 注册设备到设备服务器上
    void RegisterDeviceToServer(void);

private:
    /// 得到脚本系统所使用的资源数据
    void onProcessNetProjectGetData(QWebSocket *conn,QJsonObject &mesObj);
    /// 注册设备
    void onProcessNetProjectRegisterDevice(QWebSocket *conn,QJsonObject &mesObj);
    /// 用户命令处理
    void onProcessNetProjectUserCommand(QWebSocket *conn,QJsonObject &mesObj);
    /// 和设备建立P2P连接
    void onProcessNetP2pDevice(QWebSocket *conn,QJsonObject &mesObj);

    /// 注册所有用到的函数
    void reg_all2jss(void);
    /// 将指定文件夹转换成一个包数据
    QByteArray Convertfoldertopackage(QString filePath);
    /// 将指定的包数据转换到指定的文件夹
    bool Convertpackagetofolder(QByteArray packageData,QString filePath);
    /// 得到相应文件的MD5
    QString getFileMd5(QByteArray filedata);

    void ConvertImageToBytes(const QPixmap& image);
    QPixmap getMouseCursorIconWin();
    /// 从控制设备接收到的数据
    void signalProcessControlDeviceNetBinary(QByteArray data);

    /// 监控指定目录下所有的文件
    void Monitoringdirectory(void);

signals:
    /// 处理接收到的网络消息
    void on_signalNetText(QWebSocket *conn,QString data);
    /// 处理上传/下载数据的进度
    void on_signalOperProcess(int type,qint64 sendsize,qint64 totalsize);
    /// 处理网络二进制消息
    void on_signalNetBinary(QWebSocket *conn,QByteArray &data);
    /// 处理一个新的连接到达
    void on_signalConnectedNetMes(QWebSocket *conn);
    /// 处理一个连接关闭
    void on_signalDisconnectedNetMes(QWebSocket *conn);

    /// 发送给控制设备数据的进度
    void on_signalProcessBinaryOperProcess(int type,qint64 sendsize,qint64 totalsize);
    /// 从控制设备接收到的数据
    void on_signalControlDeviceNetBinary(QByteArray data);

    /// 处理日志消息
    void on_signalLog(QString msg,QString type);
    /// 脚本重新导入
    void on_signalreloadscript(void);
    /// 脚本导入并运行以后
    void on_signalreloadedandrunscript(void);
    /// 脚本初始化
    void on_signalsystemsetup(void);
    /// 处理JSEngine警告信息
    //void on_signaljsenginewarnings(const QList<QQmlError> &warnings);

    /// 图像数据转换成bytes
    void signal_convertImageToBytes(QByteArray imgdata);

public slots:
    void WorkingdirectoryChange(const QString &path);
    void WorkingdirectoryChanged(void);
    /// 处理收到的客户端信息
    void ReverseProxyServer_reciverPendingDatagram();

    /// 发送屏幕到控制设备上
    void HandlerTimerSendScreen(void);
    /// 发送屏幕数据到控制设备上
    void signalProcessSendScreenToControlDevice(QByteArray data);

private:
    QJSEngine m_JSEngine;                             /**< 脚本引擎 */
    QString m_currentWorkingPath;                     /**< 当前系统工作目录 */
    QVector<QString> m_allResourcesPath;              /**< 所有的资源工作路径 */
    QString m_currentRegisterDllPath;                 /**< 当前脚本注册的dll所在目录 */
    bool m_isAutoLoadScript;                          /**< 有些情况下需要资源全部加装完成后再导入 */
    bool m_isDebugMode;                               /**< 是否处于调试模式下，如果在调试模式下，资源变动也需要加载脚本 */
    QFileSystemWatcher m_pSystemWorkingPathWatcher;   /**< 用于监控系统工作目录以便于重新加载脚本 */
    QTimer m_SystemWorkingPathTimer;                  /**< 用于过滤工作目录不停改变的情况,我们只需要最后的改变 */
    CWebSocketServer m_WebSocketServer;               /**< 网络支持 */
    int m_DebugServerIPPort;                          /**< 调试服务器端口 */
    QMap<QString,tagpluginItem*> m_LibraryList;       /**< 所有注册的插件 */
    QString m_currentScriptMd5;                       /**< 当前脚本的md5码 */
    bool m_Encryptedread;                             /**< 是否支持加密读取 */
    QMap<QString,QJSValue> m_ModuleList;              /**< 支持的所有模块 */

    QUdpSocket m_ReverseProxyServer;                  /**< 反向代理服务器，用于设备之间p2p通讯 */
    QHostAddress m_ReverseProxyServerIP;              /**< 反向代理服务器IP */
    int m_ReverseProxyServerPort;                     /**< 方向代理服务器端口 */
    QHostAddress m_ControlDeviceIP;                   /**< 控制设备的IP */
    quint16 m_ControlDevicePort;                      /**< 控制设备的端口 */

    QByteArray m_recvDataBytes;                       /**< 用于处理接收到的二进制数据 */
    bool m_recvDataState;                             /**< 接收二进制数据的状态控制 */
    tagDatagramHearder m_tagDataStruct;               /**< 用于二进制数据接收处理 */

    QString m_DeviceName;                             /**< 关联设备名称 */
    int m_DeviceID;                                   /**< 关联设备ID */
    int m_UserID;
    int m_DataVersion;                                /**< 当前资源版本 */
    QString m_UserPassword;                           /**< 用户名和密码 */

    bool m_isEnableScreen;                            /**< 是否支持远程桌面 */
    QTimer m_TimerSendScreen;                         /**< 用于截屏 */
    int m_SendScreenCount;                            /**< 用于控制屏幕录制 */
    int m_ScreenWidth,m_ScreenHeight;                 /**< 屏幕录制高宽 */
    int m_refreshfrequency;                           /**< 录屏频率 */
};

#endif // CSCRIPTMANAGER_H
